{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4.1 Functions and Scoping\n",
    "\n",
    "### 4.1.1 Function Definitions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4\n"
     ]
    }
   ],
   "source": [
    "def max_val(x, y):\n",
    "    if x > y:\n",
    "        return x\n",
    "    else:\n",
    "        return y\n",
    "\n",
    "print(max_val(3, 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A function for finding roots"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def find_root(x, power, epsilon):\n",
    "    # find interval containing answer\n",
    "    if x < 0 and power % 2 == 0:\n",
    "        return None\n",
    "    low = min(-1, x)\n",
    "    high = max(1, x)\n",
    "    # use bisection search\n",
    "    ans = (high + low) / 2\n",
    "    while abs(ans ** power - x) >= epsilon:\n",
    "        if ans ** power < x:\n",
    "            low = ans\n",
    "        else:\n",
    "            high = ans\n",
    "        ans = (high + low) / 2\n",
    "    return ans"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "test is_in"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.25\n",
      "x = 0.25, power = 1, epsilon = 0.1 => Okay\n",
      "0.25\n",
      "x = 0.25, power = 1, epsilon = 0.001 => Okay\n",
      "0.0\n",
      "x = 0.25, power = 1, epsilon = 1 => Okay\n",
      "0.5\n",
      "x = 0.25, power = 2, epsilon = 0.1 => Okay\n",
      "0.5\n",
      "x = 0.25, power = 2, epsilon = 0.001 => Okay\n",
      "0.0\n",
      "x = 0.25, power = 2, epsilon = 1 => Okay\n",
      "0.625\n",
      "x = 0.25, power = 3, epsilon = 0.1 => Okay\n",
      "0.6298828125\n",
      "x = 0.25, power = 3, epsilon = 0.001 => Okay\n",
      "0.0\n",
      "x = 0.25, power = 3, epsilon = 1 => Okay\n",
      "7.9296875\n",
      "x = 8, power = 1, epsilon = 0.1 => Okay\n",
      "7.99945068359375\n",
      "x = 8, power = 1, epsilon = 0.001 => Okay\n",
      "7.4375\n",
      "x = 8, power = 1, epsilon = 1 => Okay\n",
      "2.83203125\n",
      "x = 8, power = 2, epsilon = 0.1 => Okay\n",
      "2.828460693359375\n",
      "x = 8, power = 2, epsilon = 0.001 => Okay\n",
      "2.9375\n",
      "x = 8, power = 2, epsilon = 1 => Okay\n",
      "2.005859375\n",
      "x = 8, power = 3, epsilon = 0.1 => Okay\n",
      "1.9999542236328125\n",
      "x = 8, power = 3, epsilon = 0.001 => Okay\n",
      "1.953125\n",
      "x = 8, power = 3, epsilon = 1 => Okay\n",
      "-7.9296875\n",
      "x = -8, power = 1, epsilon = 0.1 => Okay\n",
      "-7.99945068359375\n",
      "x = -8, power = 1, epsilon = 0.001 => Okay\n",
      "-7.4375\n",
      "x = -8, power = 1, epsilon = 1 => Okay\n",
      "None\n",
      "x = -8, power = 2, epsilon = 0.1 => No root exists\n",
      "None\n",
      "x = -8, power = 2, epsilon = 0.001 => No root exists\n",
      "None\n",
      "x = -8, power = 2, epsilon = 1 => No root exists\n",
      "-2.005859375\n",
      "x = -8, power = 3, epsilon = 0.1 => Okay\n",
      "-1.9999542236328125\n",
      "x = -8, power = 3, epsilon = 0.001 => Okay\n",
      "-1.953125\n",
      "x = -8, power = 3, epsilon = 1 => Okay\n"
     ]
    }
   ],
   "source": [
    "def test_find_root(x_vals, powers, epsilons):\n",
    "    for x in x_vals:\n",
    "        for power in powers:\n",
    "            for epsilon in epsilons:\n",
    "                result = find_root(x, power, epsilon)\n",
    "                print(result)\n",
    "                if result is None:\n",
    "                    val = 'No root exists'\n",
    "                else:\n",
    "                    val = 'Okay'\n",
    "                    if abs(result ** power - x) > epsilon:\n",
    "                        val = 'Bad'\n",
    "                print(f'x = {x}, power = {power}, epsilon = {epsilon} => {val}')\n",
    "\n",
    "x_vals = (0.25, 8, -8)\n",
    "powers = (1, 2, 3)\n",
    "epsilons = (0.1, 0.001, 1)\n",
    "test_find_root(x_vals, powers, epsilons)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Keyword Arguments and Default Values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "positional argument follows keyword argument (2800140839.py, line 11)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  Cell \u001b[0;32mIn[3], line 11\u001b[0;36m\u001b[0m\n\u001b[0;31m    print_name('Olga', last_name = 'Puchmajerova', False)\u001b[0m\n\u001b[0m                                                        ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n"
     ]
    }
   ],
   "source": [
    "def print_name(first_name, last_name, reverse):\n",
    "    if reverse:\n",
    "        print(f'{last_name}, {first_name}')\n",
    "    else:\n",
    "        print(f'{first_name} {last_name}')\n",
    "\n",
    "print_name('Olga', 'Puchmajerova', False)\n",
    "print_name('Olga', 'Puchmajerova', reverse = False)\n",
    "print_name('Olga', last_name = 'Puchmajerova', reverse = False)\n",
    "print_name(last_name = 'Puchmajerova', first_name = 'Olga', reverse = False)\n",
    "# print_name('Olga', last_name = 'Puchmajerova', False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "default parameter values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Olga Puchmajerova\n",
      "Puchmajerova, Olga\n",
      "Puchmajerova, Olga\n"
     ]
    }
   ],
   "source": [
    "def print_name(first_name, last_name, reverse = False):\n",
    " if reverse:\n",
    "    print(last_name + ', ' + first_name)\n",
    " else:\n",
    "    print(first_name, last_name)\n",
    "\n",
    "print_name('Olga', 'Puchmajerova')\n",
    "print_name('Olga', 'Puchmajerova', True)\n",
    "print_name('Olga', 'Puchmajerova', reverse = True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.1.3 Variable Number of Arguments"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.5\n"
     ]
    }
   ],
   "source": [
    "def mean(*args):\n",
    " # Assumes at least one argument and all arguments are numbers\n",
    " # Returns the mean of the arguments\n",
    "    tot = 0\n",
    "    for a in args:\n",
    "        tot += a\n",
    "    return tot/len(args)\n",
    "\n",
    "print(mean(4, 3, 2, 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.1.4 Scoping"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x = 4\n",
      "z = 4\n",
      "x = 3\n",
      "y = 2\n"
     ]
    }
   ],
   "source": [
    "def f(x): #name x used as formal parameter\n",
    "    y = 1\n",
    "    x = x + y\n",
    "    print('x =', x)\n",
    "    return x\n",
    "x = 3\n",
    "y = 2\n",
    "z = f(x) #value of x used as actual parameter\n",
    "print('z =', z)\n",
    "print('x =', x)\n",
    "print('y =', y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nested scopes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x=4\n",
      "z=4\n",
      "x=abc\n",
      "x=4\n",
      "x=3\n",
      "z=<function f.<locals>.g at 0x10bcd3920>\n",
      "x=abc\n"
     ]
    }
   ],
   "source": [
    "def f(x):\n",
    "    def g():\n",
    "        x = 'abc'\n",
    "        print(f'x={x}')\n",
    "    def h():\n",
    "        z = x\n",
    "        print(f'z={z}')\n",
    "    x = x + 1\n",
    "    print(f'x={x}')\n",
    "    h()\n",
    "    g()\n",
    "    print(f'x={x}')\n",
    "    return g\n",
    "\n",
    "x = 3\n",
    "z = f(x)\n",
    "print(f'x={x}')\n",
    "print(f'z={z}')\n",
    "z()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "because x is local to g, it has no value when the print statement is executed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n"
     ]
    },
    {
     "ename": "UnboundLocalError",
     "evalue": "cannot access local variable 'x' where it is not associated with a value",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mUnboundLocalError\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[11], line 9\u001b[0m\n\u001b[1;32m      7\u001b[0m f()\n\u001b[1;32m      8\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m3\u001b[39m\n\u001b[0;32m----> 9\u001b[0m \u001b[43mg\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "Cell \u001b[0;32mIn[11], line 4\u001b[0m, in \u001b[0;36mg\u001b[0;34m()\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mg\u001b[39m():\n\u001b[0;32m----> 4\u001b[0m     \u001b[38;5;28mprint\u001b[39m(\u001b[43mx\u001b[49m)\n\u001b[1;32m      5\u001b[0m     x \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n",
      "\u001b[0;31mUnboundLocalError\u001b[0m: cannot access local variable 'x' where it is not associated with a value"
     ]
    }
   ],
   "source": [
    "def f():\n",
    "    print(x)\n",
    "def g():\n",
    "    print(x)\n",
    "    x = 1\n",
    "x = 3\n",
    "f()\n",
    "x = 3\n",
    "# g()\n",
    "# cannot access local variable 'x' where it is not associated with a value"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
